package beer.xz.beer.device.comm.protocol

import java.nio.ByteBuffer
import java.nio.ByteOrder

/**
 * 啤酒机命令表
 * 根据CSV文件中的命令表实现下发命令
 */
object BeerCommandTable {
    // 协议常量
    private const val HEADER_BYTE: Byte = 0x3A  // 数据头
    private const val TAIL_BYTE1: Byte = 0x0D   // 数据尾1
    private const val TAIL_BYTE2: Byte = 0x0A   // 数据尾2
    private const val RS485_ADDRESS: Byte = 0x00 // RS485转发地址

    // 命令码
    private const val CMD_WRITE: Byte = 0x16    // 写命令 (单周期机台运行指令)

    // 子机地址
    private const val SUB_ADDR_RAM: Byte = 5    // RAM子机地址

    // 功能码
    private const val FUNC_ZERO: Int = 0x01     // 重量去皮
    private const val FUNC_DATA_TRANSFER: Int = 0x04 // 数据发送
    private const val FUNC_RUN: Int = 0x1024    // 打酒

    // 命令长度
    private const val COMMAND_LENGTH = 38       // 总字节数

    /**
     * 计算校验和
     * @param data 要计算校验和的数据
     * @return 校验和
     */
    private fun calculateCRC(data: ByteArray): Byte {
        var sum = 0
        for (element in data) {
            sum += element.toInt() and 0xFF
        }
        return (sum and 0xFF).toByte()
    }

    /**
     * 构建命令
     * @param mainAddress 主机地址
     * @param subAddress 子地址
     * @param commandCode 命令码
     * @param functionCode 功能码
     * @param data1 数据1 (长整形数)
     * @param data2 数据2 (长整形数)
     * @param data3 数据3 (单精度数)
     * @param data4 数据4 (单精度数)
     * @param data5 数据5 (单精度数)
     * @param data6 数据6 (单精度数)
     * @return 构建好的命令字节数组
     */
    private fun buildCommand(
        mainAddress: Byte,
        subAddress: Byte,
        commandCode: Byte,
        functionCode: Int,
        data1: Int,
        data2: Int,
        data3: Float,
        data4: Float,
        data5: Float,
        data6: Float
    ): ByteArray {
        val buffer = ByteBuffer.allocate(COMMAND_LENGTH)
        buffer.order(ByteOrder.LITTLE_ENDIAN)

        // 数据头
        buffer.put(HEADER_BYTE)

        // 主机地址
        buffer.put(mainAddress)

        // 子地址
        buffer.put(subAddress)

        // 命令码
        buffer.put(commandCode)

        // 功能码 (4字节)
        buffer.putInt(functionCode)

        // 数据1 (4字节)
        buffer.putInt(data1)

        // 数据2 (4字节)
        buffer.putInt(data2)

        // 数据3 (4字节)
        buffer.putFloat(data3)

        // 数据4 (4字节)
        buffer.putFloat(data4)

        // 数据5 (4字节)
        buffer.putFloat(data5)

        // 数据6 (4字节)
        buffer.putFloat(data6)

        // 计算校验和
        val dataForCRC = ByteArray(buffer.position())
        buffer.position(0)
        buffer.get(dataForCRC)
        val crc = calculateCRC(dataForCRC)

        // 校验和
        buffer.position(dataForCRC.size)
        buffer.put(crc)

        // 数据尾1
        buffer.put(TAIL_BYTE1)

        // 数据尾2
        buffer.put(TAIL_BYTE2)

        // 确保校验和不为0
        val result = buffer.array()
        if (result[dataForCRC.size] == 0.toByte()) {
            result[dataForCRC.size] = 0x01.toByte()
        }

        // 确保尾部字节正确
        result[result.size - 2] = TAIL_BYTE1
        result[result.size - 1] = TAIL_BYTE2

        return result
    }

    /**
     * 重量去皮命令 (Zero)
     * @param adcChannel ADC通道 (1-3)
     * @return 命令字节数组
     */
    fun zeroWeight(adcChannel: Int): ByteArray {
        require(adcChannel in 1..3) { "ADC通道必须在1-3范围内" }

        return buildCommand(
            mainAddress = 1,
            subAddress = SUB_ADDR_RAM,
            commandCode = CMD_WRITE,
            functionCode = FUNC_ZERO,
            data1 = adcChannel,
            data2 = 0,
            data3 = 0f,
            data4 = 0f,
            data5 = 0f,
            data6 = 0f
        )
    }

    /**
     * 数据发送开始命令
     * @param mode 发送模式 (1=USB开始, 2=USB停止, 5=RS232开始, 6=RS232停止)
     * @return 命令字节数组
     */
    fun dataTransfer(mode: Int): ByteArray {
        require(mode in listOf(1, 2, 5, 6)) { "发送模式必须是1、2、5或6" }

        return buildCommand(
            mainAddress = 1,
            subAddress = SUB_ADDR_RAM,
            commandCode = CMD_WRITE,
            functionCode = FUNC_DATA_TRANSFER,
            data1 = mode,
            data2 = 0,
            data3 = 0f,
            data4 = 0f,
            data5 = 0f,
            data6 = 0f
        )
    }

    /**
     * 打酒命令 (Run)
     * @param volume 酒量 (毫升)
     * @param flowRate 流量速度
     * @return 命令字节数组
     */
    fun pourBeer(volume: Float, flowRate: Float): ByteArray {
        return buildCommand(
            mainAddress = 1,
            subAddress = SUB_ADDR_RAM,
            commandCode = CMD_WRITE,
            functionCode = FUNC_RUN,
            data1 = 0,
            data2 = 0,
            data3 = volume,
            data4 = flowRate,
            data5 = 0f,
            data6 = 0f
        )
    }
}
